﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Zeta.Common;
using Zeta.TreeSharp;
using Action = System.Action;

namespace KadalaSpree.XCore
{
    public static partial class X
    {
        public class Hooks
        {
            public class Hook
            {
                public HookType HookType { get; set; }
                public HookMethod Method { get; set; }
                public int Position { get; set; }
                public string Name { get; set; }
                public bool Disabled { get; set; }
                public Composite Behavior { get; set; }
                public XModule Module { get; set; }
            }

            private static readonly Dictionary<Composite, Hook> HookArchive = new Dictionary<Composite, Hook>();

            public static void Register(XModule module, Composite behavior, HookType type = HookType.BotBehavior, HookMethod method = HookMethod.Before, int position = 0)
            {
                Register(new Hook
                {
                    Name = module.Name,
                    Module = module,
                    Behavior = behavior,
                    HookType = type,
                    Method = method,
                    Position = position
                });
            }

            public static void Register(Hook hook)
            {
                if (!HookArchive.ContainsKey(hook.Behavior))
                {
                    Logger.Instance.Log("Registering {0} behavior for {1} Hook", hook.Name, hook.HookType);
                    HookArchive.Add(hook.Behavior, hook);
                }
                else
                {
                    HookArchive[hook.Behavior].Disabled = false;
                }
            }

            public static void Disable(string name, HookType type)
            {
                HookArchive.Where(h => h.Value.Name == name && h.Value.HookType == type).ForEach(h => h.Value.Disabled = true);
            }

            public static void Enable(string name, HookType type)
            {
                HookArchive.Where(h => h.Value.Name == name && h.Value.HookType == type).ForEach(h => h.Value.Disabled = false);
            }

            internal static void InsertHooks()
            {
                HookArchive.Where(h => !h.Value.Disabled).ForEach(h => InsertHook(h.Value) );
            }

            internal static void RemoveHooks()
            {
                HookArchive.ForEach(h => RemoveHook(h.Value));
            }

            internal static void InsertHook(Hook hook)
            {
                var position = GetInsertPosition(hook);

                if (hook.Method == HookMethod.Inject)
                {
                    switch (hook.HookType)
                    {
                        case HookType.VendorRun:

                            Logger.Instance.Log("Injecting {0} into {1} Hook at Position {2}", hook.Name, hook.HookType, position);
                            var selector = VendorRunPrioritySelector;
                            if (selector.Children.Count >= position)
                            {
                                selector.InsertChild(position, hook.Behavior);    
                            }                                
                            break;

                        default:
                            Logger.Instance.Log("Hook Injection not supported for {0} / {1}", hook.Name, hook.HookType);
                            hook.Disabled = true;
                            break;
                    }
                }
                else
                {
                    Logger.Instance.Log("Adding {0} into {1} Hook at Position {2}", hook.Name, hook.HookType, position);
                    TreeHooks.Instance.InsertHook(hook.HookType.ToString(), position, hook.Behavior);                    
                }
            }

            private static void RemoveHook(Hook hook)
            {
                if (hook.Method == HookMethod.Inject)
                {
                    switch (hook.HookType)
                    {
                        case HookType.VendorRun:
                            VendorRunPrioritySelector.Children.Remove(hook.Behavior);
                            break;
                    }
                }
                else
                {
                    TreeHooks.Instance.RemoveHook(hook.HookType.ToString(), hook.Behavior);                             
                }         
            }

            private static int GetInsertPosition (Hook hook)
            {
                var position = 0;
                switch (hook.Method)
                {
                    case HookMethod.After:
                        position = TreeHooks.Instance.Hooks.Count(h => h.Key == hook.HookType.ToString());
                        break;

                    case HookMethod.Inject:
                        position = hook.Position;
                        break;
                }
                return position;
            }

            internal static void Instance_OnHooksCleared (object sender, EventArgs e)
            {
                // OnHooksCleared gets called both when starting and stopping the bot
                //InsertHooks();
            }

            /// <summary>
            /// Extracts the DB Town Run behavior from VendorRun hook.
            /// </summary>
            internal static PrioritySelector VendorRunPrioritySelector
            {
                get
                {
                    if (TreeHooks.Instance.Hooks.ContainsKey("VendorRun"))
                    {
                        var vendorRunDecorator = TreeHooks.Instance.Hooks["VendorRun"].First() as Decorator;

                        if (vendorRunDecorator != null)
                        {
                            if (vendorRunDecorator.DecoratedChild is Sequence)
                            {
                                //Trinity Modified TownRun
                                var vendorRunSequence = vendorRunDecorator.DecoratedChild as Sequence;
                                return vendorRunSequence.Children.First() as PrioritySelector;
                            }

                            if (vendorRunDecorator.DecoratedChild is PrioritySelector)
                            {
                                // Original Unbesmirched TownRun
                                return vendorRunDecorator.DecoratedChild as PrioritySelector;
                            }

                            Logger.Instance.Debug("Town Run Behavior not found!");
                        }
                        else
                        {
                            Logger.Instance.Debug("Town Run Behavior not found!");
                        }
                    }
                    else
                    {
                        Logger.Instance.Debug("Town Run Behavior not found!");
                    }
                    
                    return new PrioritySelector();
                }
            }

        }
    }
}
